﻿using System;
using System.IO;
using System.Xml;

using Microsoft.SharePoint;

namespace Pcubed.Shuie
{
  class Program
  {
    private const string EDIT = "edit";
    private const string DELETE = "delete";
    private const string ADD = "add";

    static void Main(string[] args)
    {
      #region testing
    /*  
      if (args.Length == 0)
      {
        String fileIn = @"C:\SVN\Product\ShUIE Addons\DeliveryHub Lifecycle (ShUIE v2)\xml entries\Pcubed DeliveryHub Lifecycle - Stage Do List layout changes.xml";
        String fileOut = @"C:\shuie\a.xml";

        ShuieXml entry = CreateShuieXmlFromFile(fileIn);
        if (entry == null)
          return;

        // check id
        if (entry.Id.Equals(Guid.Empty))
          entry.Id = Guid.NewGuid();

        // update output elements
        Console.WriteLine(entry.UpdateOutputs());
        
        ShuieContext context = new ShuieContext {
          SiteUri = "http://p3uk64p12/PWA",
          WebUri = "http://p3uk64p12/PWA/template",
          WebTitle = "template",
          ListTitle = String.Empty,
          BaseTemplate = String.Empty,
          IsInvalidMode = true,
          IsNewMode = false,
          IsEditMode = false,
          IsDisplayMode = false
        };

        Console.WriteLine(String.Format("Works in context: {0}", entry.CheckContext(context)));       

        using (TextWriter tw = new StreamWriter(fileOut, false))
          tw.Write(entry.OuterXml);

        // add property bag in SharePoint
        String url = "http://p3uk64p12/PWA";
        SaveInPropertyBag(url, entry.Title, entry.OuterXml, ADD);

        // get value from SharePoint, to make sure it worked
        entry = GetShuieXmlFromPropertyBag(url, entry.Id.ToString());
        okWriteLine(String.Format("{0} - successfully added", entry.Title));

        return;
      }
      */
      #endregion

      #region Command Line Arg Parsing
      String showHelp = String.Empty;

      if (args.Length > 0 && args[0].Equals("-o"))
      {
        if (args.Length > 3 && args[2].Equals("-url"))
        {
          switch (args[1])
          {
            case "activateentry":
              if (args.Length == 6 && args[4].Equals("-entryuid"))
                SetActiveFlag(args[3], args[5], true);
              else
                showHelp = "activateentry";
              break;
            case "addentry":
              if (args.Length == 6 && args[4].Equals("-filename"))
                AddEntry(args[3], args[5]);
              else
                showHelp = "addentry";
              break;
            case "deactivateentry":
              if (args.Length == 6 && args[4].Equals("-entryuid"))
                SetActiveFlag(args[3], args[5], false);
              else
                showHelp = "deactivateentry";
              break;
            case "deleteentry":
              if (args.Length == 6 && args[4].Equals("-entryuid"))
                DeleteEntry(args[3], args[5]);
              else
                showHelp = "deleteentry";
              break;
            case "downloadentry":
              if (args.Length == 8 && args[4].Equals("-entryuid") && args[6].Equals("-filename"))
                DownloadEntry(args[3], args[5], args[7]);
              else
                showHelp = "downloadentry";
              break;
            case "enumentries":
              if (args.Length == 4)
                EnumEntries(args[3]);
              else
                showHelp = "enumentries";
              break;
            case "setorder":
              Int32 order = 50;
              if (args.Length == 8 && args[4].Equals("-entryuid") && args[6].Equals("-order") && Int32.TryParse(args[7], out order))
                SetOrder(args[3], args[5], order);
              else
                showHelp = "setorder";
              break;
            default:
              showHelp = "main";
              break;
          }
        }
        else if (args.Length == 4 && args[1].Equals("templatexml") && args[2].Equals("-filename"))
          TemplateXml(args[3]);
        else
          showHelp = "main";
      }
      else if (args.Length > 1 && args[0].Equals("-help"))
        showHelp = args[1];
      else
        showHelp = "main";
      #endregion
      
      #region Help
      if (!showHelp.Equals(String.Empty))
      {
        ConsoleColor original = Console.ForegroundColor;
        Console.ForegroundColor = ConsoleColor.Yellow;
        switch (showHelp)
        {
          case "main":
            Console.Write(@"
Usage:
ShUIE.Admin.exe -o <operation> [<parameters>]
ShUIE.Admin.exe -help <operation>

Operations and parameters:
          activateentry -url <value> -entryuid <value>
          addentry -url <value> -filename <value>
          deactivateentry -url <value> -entryuid <value>
          deleteentry -url <value> -entryuid <value>
          downloadentry -url <value> -entryuid <value> -filename <value>
          enumentries -url <value>
          setorder -url <value> -entryuid <value> -order <value>

          templatexml -filename <value>

Examples:
          ShUIE.Admin.exe -o activateentry -url http://p3uk64p12/pwa -entryuid ""4e3cb3fc-36a5-40c3-b3f9-bd82ca9e6c4""
          ShUIE.Admin.exe -o addentry -url http://p3uk64p12/pwa -filename ""c:\test.xml""
          ShUIE.Admin.exe -o templatexml -filename ""c:\test.xml""

");
            break;
          case "activateentry":
            Console.Write(@"
Usage:
ShUIE.Admin.exe -o activateentry -url <value> -entryuid <value>

Parameters:
        url:     url filename
        entryuid: unique identifier

Examples:
ShUIE.Admin.exe -o activateentry -url http://p3uk64p12/pwa -entryuid ""4e3cb3fc-36a5-40c3-b3f9-bd82ca9e6c4""

");
            break;
          case "addentry":
            Console.Write(@"
Usage:
ShUIE.Admin.exe -o addentry -url <value> -filename <value>

Parameters:
        url:     url filename
        filename:    xml filename

Examples:
ShUIE.Admin.exe -o addentry -url http://p3uk64p12/pwa -filename ""c:\test.xml""

");
            break;
          case "deactivateentry":
            Console.Write(@"
Usage:
ShUIE.Admin.exe -o deactivateentry -url <value> -entryuid <value>

Parameters:
        url:     url filename
        entryuid: unique identifier

Examples:
ShUIE.Admin.exe -o deactivateentry -url http://p3uk64p12/pwa -entryuid ""4e3cb3fc-36a5-40c3-b3f9-bd82ca9e6c4""

");
            break;
          case "deleteentry":
            Console.Write(@"
Usage:
ShUIE.Admin.exe -o deleteentry -url <value> -entryuid <value>

Parameters:
        url:     url filename
        entryuid: unique identifier

Examples:
ShUIE.Admin.exe -o deleteentry -url http://p3uk64p12/pwa -entryuid ""4e3cb3fc-36a5-40c3-b3f9-bd82ca9e6c4""

");
            break;
          case "downloadentry":
            Console.Write(@"
Usage:
ShUIE.Admin.exe -o downloadentry -url <value> -entryuid <value> -filename <value>

Parameters:
        url:     url filename
        entryuid: unique identifier
        filename:    filename to file to be created

Examples:
ShUIE.Admin.exe -o downloadentry -url http://p3uk64p12/pwa -entryuid ""4e3cb3fc-36a5-40c3-b3f9-bd82ca9e6c4"" -filename ""c:\\test.xml""

");
            break;
          case "enumentries":
            Console.Write(@"
Usage:
ShUIE.Admin.exe -o enumentries -url <value>

Parameters:
        url:     url filename

Examples:
ShUIE.Admin.exe -o enumentries -url http://p3uk64p12/pwa

");
            break;
          case "setorder":
            Console.Write(@"
Usage:
ShUIE.Admin.exe -o setorder -url <value> -entryuid <value> -order <value>

Parameters:
        url:     url filename
        entryuid: unique identifier
        order:   order in which to run scripts

Examples:
ShUIE.Admin.exe -o setorder -url http://p3uk64p12/pwa -entryuid ""4e3cb3fc-36a5-40c3-b3f9-bd82ca9e6c4"" -order 1

");
            break;
          default:
            Console.Write(@"

Command not available.

");
            break;
        }
        Console.ForegroundColor = original;
      }
      #endregion
    }
     
    /// <summary>
    /// Change the active flag of an entry
    /// which make the entry work run or not in SharePoint
    /// 
    /// Used in "activeateentry" and "deactivateentry" commands
    /// </summary>
    /// <param name="url">site collection url</param>
    /// <param name="id">entry id</param>
    /// <param name="newValue">true | false</param>
    private static void SetActiveFlag(String url, String id, Boolean newValue)
    {
      try
      {
        ShuieXml entry = GetShuieXmlFromPropertyBag(url, id);

        infoWriteLine(String.Format("{0} - previous state (Active): {1}", entry.Title, entry.IsActive));

        entry.IsActive = newValue;

        // update property bag in SharePoint
        SaveInPropertyBag(url, entry.Title, entry.OuterXml, EDIT);
        // get value from SharePoint, to make sure it worked
        entry = GetShuieXmlFromPropertyBag(url, id);

        okWriteLine(String.Format("{0} - new state (Active): {1}", entry.Title, entry.IsActive));
      }
      catch (Exception ex)
      {
        exceptionWriteLine(ex);
      }
    }

    /// <summary>
    /// Adds a new entry
    /// </summary>
    /// <param name="url">site collection url</param>
    /// <param name="filename">file to be uploaded</param>
    /// NOTE: Id will NOT be overwrite if it exists in the xml
    private static void AddEntry(String url, String filename)
    {
      try
      {
        ShuieXml entry = CreateShuieXmlFromFile(filename);

        if (entry == null)
          throw new Exception(String.Format("{0} - NOT added", filename));

        // check id
        if (entry.Id.Equals(Guid.Empty))
          entry.Id =Guid.NewGuid();

        // update output elements
        String warn = entry.UpdateOutputs();
        if (!warn.Equals(String.Empty))
          infoWriteLine(warn);

        // add property bag in SharePoint
        SaveInPropertyBag(url, entry.Title, entry.OuterXml, ADD);

        // get value from SharePoint, to make sure it worked
        entry = GetShuieXmlFromPropertyBag(url, entry.Id.ToString());
        okWriteLine(String.Format("{0} - successfully added", entry.Title));
      }
      catch (Exception ex)
      {
        exceptionWriteLine(ex);
      }
    }

    /// <summary>
    /// Deletes an entry from the site collection's property bag
    /// </summary>
    /// <param name="url">site collection url</param>
    /// <param name="id">entry id</param>
    private static void DeleteEntry(String url, String id)
    {
      try
      {
        ShuieXml entry = GetShuieXmlFromPropertyBag(url, id);

        // delete property bag in SharePoint
        SaveInPropertyBag(url, entry.Title, entry.OuterXml, DELETE);

        // get value from SharePoint, to make sure it worked
        if(GetShuieXmlFromPropertyBag(url, id) == null)
          okWriteLine(String.Format("{0} - successfully removed", entry.Title));
        else
          infoWriteLine(String.Format("{0} - was NOT removed!", entry.Title));
      }
      catch (Exception ex)
      {
        exceptionWriteLine(ex);
      }
    }

    /// <summary>
    /// Downloads a copy of a entry
    /// </summary>
    /// <param name="url">site collection url</param>
    /// <param name="id">entry id</param>
    /// <param name="filename">file to be created or overwrited</param>
    private static void DownloadEntry(String url, String id, String filename)
    {
      try
      {
        ShuieXml entry = GetShuieXmlFromPropertyBag(url, id);

        entry.DeleteOutputs();

        using (TextWriter tw = new StreamWriter(filename, false))
          tw.Write(entry.OuterXml);

        okWriteLine(String.Format("{0} - successfully downloaded.", entry.Title));
      }
      catch (Exception ex)
      {
        exceptionWriteLine(ex);
      }
    }

    /// <summary>
    /// Shows all existing ShUIE entries
    /// </summary>
    /// <param name="url">site collection url</param>
    private static void EnumEntries(String url)
    {
      try
      {
        using (SPWeb web = new SPSite(url).RootWeb)
        {
          foreach (String key in web.Properties.Keys)
            if (key.StartsWith("shuie_"))
            {
              ShuieXml entry = new ShuieXml();
              entry.LoadXml(web.Properties[key]);

              Console.WriteLine();
              Console.WriteLine(String.Format("title : {0}{1}uid   : {2}{3}active: {4}",
                entry.Title, Environment.NewLine,
                entry.Id, Environment.NewLine,
                entry.IsActive));
            }
        }
      }
      catch (Exception ex)
      {
        exceptionWriteLine(ex);
      }
    }

    /// <summary>
    /// Update the order in which a script is rendered
    /// </summary>
    /// <param name="url">site collection url</param>
    /// <param name="id">entry id</param>
    /// <param name="order">order to be rendered</param>
    private static void SetOrder(String url, String id, Int32 newOrder)
    {
      try
      {
        ShuieXml entry = GetShuieXmlFromPropertyBag(url, id);

        infoWriteLine(String.Format("{0} - previous state (Order): {1}", entry.Title, entry.Order));

        entry.Order = newOrder;

        // update property bag in SharePoint
        SaveInPropertyBag(url, entry.Title, entry.OuterXml, EDIT);
        // get value from SharePoint, to make sure it worked
        entry = GetShuieXmlFromPropertyBag(url, id);

        okWriteLine(String.Format("{0} - new state (Order): {1}", entry.Title, entry.Order));
      }
      catch (Exception ex)
      {
        exceptionWriteLine(ex);
      }
    }

    /// <summary>
    /// Creates an empty ShUIE xml
    /// </summary>
    /// <param name="filename">new xml file filename</param>
    private static void TemplateXml(String filename)
    {
      try
      {
        String templatexml = @"
<?xml version=""1.0"" encoding=""UTF-8""?>
<shuie order=""50"" active=""true"">
  <title><![CDATA[sample]]></title>
  <contexts>
    <context active=""true"">
      <siteUri><![CDATA[http://localhost/pwa]]></siteUri>
      <webUri><![CDATA[http://localhost/pwa]]></webUri>
      <webTitle><![CDATA[Project Web Access]]></webTitle>
      <listTitle><![CDATA[list]]></listTitle>
      <baseTemplate><![CDATA[Document Library]]></baseTemplate>
      <userLogin><![CDATA[domain\user]]></userLogin>
      <userName><![CDATA[user]]></userName>
      <userIsSiteAdmin>false</userIsSiteAdmin>
      <userIsWebAdmin>false</userIsWebAdmin>
      <pageMode>
        <invalid>false</invalid>
        <new>false</new>
        <edit>false</edit>
        <display>false</display>
      </pageMode>
    </context>
  </contexts>
  <javascript minify=""true"">
    <code><![CDATA[$(document).ready(function() {$(""#GlobalTitleAreaImage"").append(""<table class='sample'><tbody><tr><td><a href='http://www.pcubed.com'>"" + sample.textValue + ""</a></td></tr></tbody></table>"");});]]>
    </code>
  </javascript>
  <styles>
		<css minify=""true"" mediatype=""screen"">
	    <code><![CDATA[.sample{font-size:14pt;}]]></code>
		</css>
	</styles>
  <json minify=""true"">
    <code><![CDATA[var sample = {textValue:""TEST""}]]></code>
  </json>
</shuie>
";

        using (TextWriter tw = new StreamWriter(filename, false))
          tw.Write(templatexml);

        okWriteLine("Empty xml successfully created.");
      }
      catch (Exception ex)
      {
        exceptionWriteLine(ex);
      }
    }
    
    #region Utilities
    /// <summary>
    /// Updates ShUIE entries in SharePoint site collection's property bag
    /// </summary>
    /// <param name="siteUri">site collection url</param>
    /// <param name="title">title</param>
    /// <param name="xml">xml value</param>
    /// <param name="action">add | edit | delete</param>
    private static void SaveInPropertyBag(String siteUri, String title, String xml, String action)
    {
      using (SPWeb siteCollectionWeb =  new SPSite(siteUri).RootWeb)
      {
        if(title.Equals(String.Empty))
          title = Guid.NewGuid().ToString();

        title = String.Format("shuie_{0}", title.Replace(" ","_").Trim().ToLowerInvariant());

        switch (action)
        {
          case ADD:
          case EDIT:
            if (siteCollectionWeb.AllProperties.ContainsKey(title))
              siteCollectionWeb.AllProperties[title] = xml;
            else
              siteCollectionWeb.AllProperties.Add(title, xml);
            break;
          case DELETE:
            siteCollectionWeb.AllProperties[title] = null;
            siteCollectionWeb.AllProperties.Remove(title);
            break;
          default:
            break;
        }
        SPWebUpdate(siteCollectionWeb);
      }
    }

    private static void SPWebUpdate(SPWeb oSPWeb)
    {
      if (!oSPWeb.AllowUnsafeUpdates)
      {
        oSPWeb.AllowUnsafeUpdates = true;
        oSPWeb.Update();
        oSPWeb.AllowUnsafeUpdates = false;
      }
      else
        oSPWeb.Update();
    }

    private static ShuieXml GetShuieXmlFromPropertyBag(String url, String id)
    {
      using (SPWeb web = new SPSite(url).RootWeb)
      {
        foreach (String key in web.Properties.Keys)
          if (key.StartsWith("shuie_"))
          {
            ShuieXml entry = new ShuieXml();
            entry.LoadXml(web.Properties[key]);

            if (entry.Id.Equals(new Guid(id)))
              return entry;
          }
      }

      return null;
    }

    private static ShuieXml CreateShuieXmlFromFile(String filename)
    {
      ShuieXml entry = null;

      //Validate the valid XML with the XSD 
      XmlValidator xmlValidator = new XmlValidator();
      if (xmlValidator.ValidateXml(filename))
      {
        entry = new ShuieXml();
        entry.LoadXml(((TextReader)new StreamReader(filename)).ReadToEnd());
      }
      else
        exceptionWriteLine(new XmlException(xmlValidator.Error));

      return entry;
    }

    private static void exceptionWriteLine(Exception ex)
    {
      ConsoleColor original = Console.ForegroundColor;
      Console.ForegroundColor = ConsoleColor.Red;
      Console.WriteLine(String.Format("ERROR: {0}", ex.Message));
      Console.ForegroundColor = original;
    }
    
    private static void okWriteLine(String text)
    {
      ConsoleColor original = Console.ForegroundColor;
      Console.ForegroundColor = ConsoleColor.Green;
      Console.WriteLine(text);
      Console.ForegroundColor = original;
    }

    private static void infoWriteLine(String text)
    {
      ConsoleColor original = Console.ForegroundColor;
      Console.ForegroundColor = ConsoleColor.Yellow;
      Console.WriteLine(text);
      Console.ForegroundColor = original;
    }
    #endregion
  }
}
